package ws.very.util.json

import org.json4s.JValue
import org.json4s.NoTypeHints
import org.json4s.jvalue2extractable
import org.json4s.jvalue2monadic
import org.json4s.native.JsonMethods.parse
import org.json4s.native.Serialization
import org.json4s.native.Serialization.write
import org.json4s.string2JsonInput
import org.json4s.JsonDSL
import org.json4s.DoubleMode
import scala.util.Try
import scala.reflect.ClassTag
import org.json4s.JsonAST.JNumber
import ws.very.util.lang.Texts

trait JsonHelper extends JsonDSL {

  import org.json4s.native.Serialization.{read, write}
  import org.json4s.native.Serialization
  import org.json4s._
  import org.json4s.native.JsonMethods._

  implicit val formats = Serialization.formats(NoTypeHints)

  //type DslConversion = T => JValue
  implicit def toJson[T <% JValue](some: T) = compact(render(some))

  def parseJson(s: S) = parse(s)

  object Json {
    def unapply(jsonStr: S) = Try {
      parseJson(jsonStr)
    }.toOption

  }

  object JsonOps {

    def get[R](jVal: JValue, k: S)(implicit m: Manifest[R]): R =
      get(k)(m, jVal)

    def getNum(jVal: JValue, k: S): Number =
      getNum(k)(jVal)

    def get[R: Manifest](k: S)(implicit jVal: JValue) =
      (jVal \ k).extract[R]

    def getNum(k: S)(implicit jVal: JValue) =
      get[Number](jVal, k)

    def \(k: S)(implicit jVal: JValue) = (jVal \ k)

    def push[R](implicit jVal: JValue, m: Manifest[R]) =
      jVal.extract[R]
  }

  object JsonExtractors {

    import JsonOps._

    implicit def toLong(b: BigInt) = b.toLong

    implicit def toInt(b: BigInt) = b.toInt

    def param(key: S) = { implicit j: JValue => Try {
      \(key)(j)
    }.toOption
    }.toCase

    @deprecated
    def parm(k: S) = param(k)

    def jValCollect[T <: JValue : ClassTag] = { j: JValue => O(j).collect { case t: T => t.values } }.toCase

    val str = jValCollect[JString].andThen {
      _.filter {
        !_.trim().isEmpty()
      }
    }.toCase
    val option = { j: JValue => Some(j.toOption) }.toCase
    val array = { j: JValue => O(j).collect { case JArray(v) => v } }.toCase
    //option.andThen(_.collect { case JArray(v) => v }).toCase
    val arrayFirst = array.andThen {
      _.map { array =>
        array.head
      }
    }.toCase


    val nonEmptyArray = array.andThen {
      _.collect({ case a if a => a })
    }.toCase
    val strOption = option.andThen {
      _.map {
        _.collect { case str(s) => s }
      }
    }.toCase
    val arrayOption = option.andThen {
      _.map {
        _.collect { case array(s) => s }
      }
    }.toCase
    val strs = array.toPfn[JValue].andThen {
      _.collect {
        str.toPfn[JValue]
      }
    }.lift.toCase
    val bool = jValCollect[JBool]
    //    val long = jValCollect[J]
    //    val longOption = option.andThen { _.map { _.collect { case long(i) => i } } }.toCase
    val int = jValCollect[JInt]
    val intOption = option.andThen {
      _.map {
        _.collect { case int(i) => i }
      }
    }.toCase
    //double在int下无法解析
    val double = option.toPfn[JValue].andThen(_.collect {
      case JInt(i) => i.toDouble
      case JDouble(d) => d
      case JLong(l) => l.toDouble
      case JDecimal(d) => d.toDouble
    }).toCase
    val decimal = option.toPfn[JValue].andThen(_.collect {
      case JString(v) => BigDecimal(v)
      case JInt(i) => BigDecimal(i)
      case JDouble(d) => BigDecimal(d)
      case JLong(l) => BigDecimal(l)
      case JDecimal(d) => d
    }).toCase
    val doubleOption = option.andThen {
      _.map {
        _.collect { case double(s) => s }
      }
    }.toCase
    val decimalOption = option.andThen {
      _.map {
        _.collect { case decimal(s) => s }
      }
    }.toCase
    val mobileNo = str.and { (in, out) => out.filter {
      _.matches(Texts.Regex.Num)
    }.orElse(int(in).map {
      _.toString
    })
    }.toCase

    def extraClazz[A](implicit formats: Formats, mf: scala.reflect.Manifest[A]) = { j: JValue =>
      j.extractOpt[A](formats, mf)
    }.toCase

  }

}

trait JsonHelperWithDoubleMode extends JsonHelper with DoubleMode {

}